/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.commons;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JButton;
import javax.swing.JPanel;
import org.balisunrise.annotation.ActionCancel;
import org.balisunrise.annotation.ActionConfirm;
import org.balisunrise.application.Application;
import org.balisunrise.builder.CommonFactory;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.DAO;
import org.balisunrise.common.Editor;
import org.balisunrise.common.Event;
import org.balisunrise.common.Mode;
import org.balisunrise.common.Search;
import org.balisunrise.common.VisualAction;
import org.balisunrise.common.VisualEntityType;
import org.balisunrise.common.abstracts.AbstractVisualEntity;
import org.balisunrise.common.events.TransactionModeChangedEvent;
import org.balisunrise.common.events.VisualActionEvent;
import org.balisunrise.common.listeners.TransactionActionsListener;
import org.balisunrise.common.listeners.TransactionListener;
import org.balisunrise.common.listeners.TransactionModeListener;
import org.balisunrise.swing.components.panel.TransactionActionPanel;

/**
 *
 * @author Glauco
 */
public class TransactionActions extends AbstractVisualEntity
        implements org.balisunrise.common.TransactionActions
        <Transaction, VisualAction, TransactionActionPanel>{

    public static final String PROP_TRANSACTION = "transaction";
    public static final String PROP_MODE = "mode";
    public static final String PROP_VISUAL_REPRESENTATION =
            "visualRepresentation";

    private Transaction transaction;
    private Method createMethod;
    private List<VisualAction> actions;
    private int oldMode;
    private TransactionActionPanel visualRepresentation;
    private List<TransactionModeListener> tmListener;
    private List<TransactionActionsListener> taListeners;

    public TransactionActions(){
        actions = new ArrayList<VisualAction>();
        tmListener = new ArrayList<TransactionModeListener>();
        taListeners = new ArrayList<TransactionActionsListener>();
    }

    public void setVisualRepresentation(TransactionActionPanel 
            visualRepresentation) {

        TransactionActionPanel old = this.visualRepresentation;
        this.visualRepresentation = visualRepresentation;
        changeSupport.firePropertyChange(PROP_VISUAL_REPRESENTATION, old,
                visualRepresentation);
    }

    public int getMode() {
        return mode;
    }

    @Override
    public void setMode(int mode) {
        if(visualRepresentation != null)
            visualRepresentation.selectButton(mode);
        super.setMode(mode);
    }

    public Transaction getTransaction() {
        return transaction;
    }

    @Override
    public void setCreateMethod(Method createMethod){
        this.createMethod = createMethod;
    }

    @Override
    public TransactionActionPanel getVisualRepresentation() {
        return visualRepresentation;
    }

    @Override
    public void setTransaction(Transaction transaction) {
        Transaction old = this.transaction;
        this.transaction = transaction;
        changeSupport.firePropertyChange(PROP_TRANSACTION, old, transaction);
    }

     @Override
    public void addModeChangedListener(TransactionModeListener tl) {
        tmListener.add(tl);
    }

    @Override
    public boolean removeModeChangedListener(TransactionModeListener tl) {
        try{
            return tmListener.remove(tl);
        }catch(Exception ex){
            Application.get().getLogger().log(
                    Level.SEVERE, ex.getMessage(), ex);
        }
        return false;
    }

    @Override
    public TransactionModeListener[] getModeChangedListeners() {
        return tmListener.toArray(new TransactionModeListener[]{});
    }

    @Override
    public void addTransactionActionsListener(TransactionActionsListener tl) {
        taListeners.add(tl);
    }

    @Override
    public boolean removeTransactionActionsListener(TransactionActionsListener tl) {
        return taListeners.remove(tl);
    }

    @Override
    public TransactionActionsListener[] getTransactionActionsListeners() {
        return taListeners.toArray(new TransactionActionsListener[]{});
    }

    @Override
    public void addAction(VisualAction action) {
        actions.add(action);
        if(visualRepresentation != null)
            visualRepresentation.addAction(
                    (JButton) action.getVisualRepresentation(),
                    getActions().length);
    }

    @Override
    public void addAction(VisualAction action, int index) {

        if (index < 0 || action == null)
            return;

        if (index >= actions.size())
            actions.add(action);
        else
            actions.add(index, action);

        if (visualRepresentation != null)
            visualRepresentation.addAction(
                    (JButton) action.getVisualRepresentation(),
                    getActions().length);
    }

    @Override
    public VisualAction[] getActions() {

        if (actions != null)
            return actions.toArray(new VisualAction[]{});

        return null;
    }

    @Override
    public void performAction(int index) {

        if (index < 0 || index > actions.size())
            return;

        final VisualAction va = actions.get(index);

        VisualActionEvent evt = null;
        try{
            evt = Application.get().getFactory()
                    .createVisualActionEvent(this, va);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransactionAction não pode criar VisualActionEvent", ex);
        }

        va.perform(evt);

        for (TransactionActionsListener l : taListeners) {
            l.VisualActionPerformed(evt);
        }
        getOwner().actionPerformed(evt);
    }

    private void transactionActionPerformed(Event evt){
        // VISUAL ACTION PERFORMED
        for (TransactionActionsListener l : taListeners) {
            l.VisualActionPerformed(evt);
        }
        getOwner().actionPerformed(evt);
    }

    @ActionConfirm(alias="", text="Confirmar", toolTipText="Confirmar")
    public void actionConfirm() {
        switch (mode) {
            case Mode.SEARCH:
                confirmSearch();
                break;
            case Mode.NEW:
                confirmNew();
                break;
            case Mode.UPDATE:
                confirmEdit();
                break;
            case Mode.DELETE:
                confirmDelete();
                break;
        }
    }

    @ActionCancel(alias = "", text = "Cancelar", toolTipText="Cancelar")
    public void actionCancel() {
        switch (mode) {
            case Mode.SEARCH:
                cancelSearch();
                break;
            case Mode.NEW:
                cancelNew();
                break;
            case Mode.UPDATE:
                cancelEdit();
                break;
            case Mode.DELETE:
                cancelDelete();
                break;
        }
    }

    @Override
    public void performSearch() {
        if (mode == Mode.SEARCH) {

            // mostra a busca
            getTransaction().showSearch();

            // Ativa eventos dos listners
            fireTransactionModeChanged();

        }
    }

    @Override
    public void performNew() {

        if (mode != Mode.SEARCH)
            return ;

        Editor<?, ?, ?, JPanel> editor = getTransaction().getEditor();

        if(editor == null)
            return ;

        setMode(Mode.NEW);
        getTransaction().showEditor();
        editor.setEntity(newEntity());
            
        org.balisunrise.common.EntityPanel<JPanel> entityPanel =
                editor.getEntityPanel();

        entityPanel.getVisualRepresentation().setEnabled(true);
            
        // Ativa eventos dos listners
        fireTransactionModeChanged();
    }

    @Override
    public void performEdit() {

        if (mode != Mode.SEARCH)
            return ;

        Search<?, ?, ?, JPanel> search = getTransaction().getSearch();
        Editor<?, ?, ?, JPanel> editor = getTransaction().getEditor();

        if(search == null || editor == null)
            return ;

        Serializable entity = search.getSelectedEntity();

        if (entity != null) {

            setMode(Mode.UPDATE);
            getTransaction().showEditor();
            editor.setEntity(entity);

            org.balisunrise.common.EntityPanel<JPanel> entityPanel =
                editor.getEntityPanel();

            entityPanel.getVisualRepresentation().setEnabled(true);

            // Ativa eventos dos listners
            fireTransactionModeChanged();
        }
    }

    @Override
    public void performDelete() {

        if (mode != Mode.SEARCH)
            return ;

        Search<?, ?, ?, JPanel> search = getTransaction().getSearch();
        Editor<?, ?, ?, JPanel> editor = getTransaction().getEditor();

        if(search == null || editor == null)
            return ;

        Serializable entity = search.getSelectedEntity();

        if (entity != null) {
            setMode(Mode.DELETE);
            getTransaction().showEditor();
            editor.setEntity(entity);
            
            org.balisunrise.common.EntityPanel<JPanel> entityPanel = 
                    editor.getEntityPanel();

            entityPanel.getVisualRepresentation().setEnabled(false);

            // Ativa eventos dos listners
            fireTransactionModeChanged();
        }
    }

    private void transactionBeforeValidate(){
        // TRANSACTION BEFORE VALIDATE
        CommonFactory factory = Application.get().getFactory();
        Event evt = null;
        try{
            evt = factory.createBeforeValidateEvent(transaction);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransacionActions não pode criar BeforeValidateEvent", ex);
        }
        for (TransactionListener l : transaction.getTransactionListeners()) {
            l.beforeValidade(evt);
        }
        transaction.getOwner().actionPerformed(evt);
    }

    private void transactionBeforeSave(){
        // TRANSACTION BEFORE SAVE
        CommonFactory factory = Application.get().getFactory();
        Event evt = null;
        try{
            evt = factory.createBeforeSaveEvent(transaction);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransactionActions não pode criar BeforeSaveEvent", ex);

        }
        for (TransactionListener l : transaction.getTransactionListeners()) {
            l.beforeSave(evt);
        }
        transaction.getOwner().actionPerformed(evt);
    }

    private void transactionAfterSave(){
        // TRANSACTION AFTER SAVE
        CommonFactory factory = Application.get().getFactory();
        Event evt = null;
        try{
            evt = factory.createAfterSaveEvent(transaction);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransactionActions não pode criar AfterSaveEvent", ex);
        }
        for (TransactionListener l : transaction.getTransactionListeners()) {
            l.afterSave(evt);
        }
        transaction.getOwner().actionPerformed(evt);
    }

    private void transactionBeforeDelete(){
        // TRANSACTION BEFORE DELETE
        CommonFactory factory = Application.get().getFactory();
        Event evt = null;
        try{
            evt = factory.createBeforeDeleteEvent(transaction);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransactionAction não pode criar BeforeDeleteEvent", ex);

        }
        for (TransactionListener l : transaction.getTransactionListeners()) {
            l.beforeDelete(evt);
        }
        transaction.getOwner().actionPerformed(evt);
    }

    private void transactionAfterDelete(){
        // TRANSACTION AFTER DELETE
        CommonFactory factory = Application.get().getFactory();
        Event evt = null;
        try{
            evt = factory.createAfterDeleteEvent(transaction);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransactionActions não pode criar AfterDeleteEvent", ex);

        }
        for (TransactionListener l : transaction.getTransactionListeners()) {
            l.afterDelete(evt);
        }
        transaction.getOwner().actionPerformed(evt);
    }

    private void confirmSearch() {
        performEdit();
    }

    private void confirmNew() {

        transactionBeforeValidate();

        if (getTransaction().getEditor().getEntityPanel().isEntityValid() &&
                !getTransaction().getEditor().getMessenger().hasError()) {

            transactionBeforeSave();

            DAO dao = getDAO();
            dao.save(getTransaction().getEditor().getEntity());
            setMode(Mode.SEARCH);
            performSearch();

            transactionAfterSave();
        }
    }

    private void confirmEdit() {

        Editor<?, ?, ?, JPanel> editor = getTransaction().getEditor();

        if(editor == null){
            Application.get().getLogger().log(Level.SEVERE,
                    "Falha em confirmEdit: Editor é nulo. EntityClass: " +
                    getEntityClass().getName());
            return ;
        }

        EntityPanel ep = (EntityPanel) editor.getEntityPanel();
        Messenger messenger = (Messenger) editor.getMessenger();

        if(ep == null){
            Application.get().getLogger().log(Level.SEVERE,
                    "Falha em confirmEdit: EntityPanel é nulo. EntityClass: " +
                    getEntityClass().getName());
            return ;
        }

        if(ep == null){
            Application.get().getLogger().log(Level.SEVERE,
                    "Falha em confirmEdit: Messenger é nulo. EntityClass: " +
                    getEntityClass().getName());
            return ;
        }

        transactionBeforeValidate();

        if (ep.isEntityValid() && !messenger.hasError()) {
            
            transactionBeforeSave();

            DAO dao = getDAO();
            dao.save(editor.getEntity());
            setMode(Mode.SEARCH);
            performSearch();

            transactionAfterSave();
        }
    }

    private void confirmDelete() {

        transactionBeforeDelete();

        DAO dao = getDAO();
        dao.delete(getTransaction().getEditor().getEntity());
        setMode(Mode.SEARCH);
        performSearch();

        transactionAfterDelete();
    }

    private void cancelSearch() {
        getTransaction().getSearch().newSearch();
    }

    private void cancelNew() {
        setMode(Mode.SEARCH);
        performSearch();
    }

    private void cancelEdit() {
        setMode(Mode.SEARCH);
        performSearch();
    }

    private void cancelDelete() {
        setMode(Mode.SEARCH);
        performSearch();
    }

    private Serializable newEntity() {

        if (createMethod != null)
            try{
                Object o = createMethod.invoke(null);
                if (getEntityClass().isInstance(o))
                    return (Serializable) o;
            }catch(Exception ex){
                Application.get().getLogger().log(Level.SEVERE,
                        "Transaction Action não pode criar nova entidade", ex);

            }

        try {
            return (Serializable) getTransaction().getEntityClass().newInstance();
        } catch (Exception e) {
            Application.get().getLogger().log(Level.SEVERE,
                    "Transaction Action não pode criar nova entidade", e);
        }
        return null;
    }

    private void fireTransactionModeChanged(){

        if (tmListener.isEmpty())
            return ;
        
        TransactionModeChangedEvent evt = null;

        try{
            evt = Application.get().getFactory()
                    .createTransactionModeChangedEvent(transaction, oldMode, mode);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "TransactionAction não pode criar TransactionModeChangedEvent",
                    ex);
        }

        for (TransactionModeListener tml : tmListener) {
            tml.transactionModeChanged(evt);
        }
        getOwner().actionPerformed(evt);
    }

    /**
     * Retorna o tipo do VisualEntity.<BR>
     * Sempre retornará TRANSACTION_ACTIONS.
     * @return VisualEntityType.TRANSACTION_ACTIONS
     */
    @Override
    public VisualEntityType getVisualEntityType() {
        return VisualEntityType.TRANSACTION_ACTIONS;
    }

    @Override
    public void actionPerformed(Event evt) {
        if (evt instanceof VisualActionEvent){
            VisualActionEvent vae = (VisualActionEvent) evt;
            if (vae.getVisualAction().getOwner() == this){
                transactionActionPerformed(evt);
                return ;
            }
        }
        super.actionPerformed(evt);
    }


}
